float radius = 150; //sphere radius
float force = -2000000; //the force with which electrons repel eachother
int tnelectrons = 100; //the desired number of electrons
int tnfaces = tnelectrons*2-4; //the total number of faces
int tiling_start = 900; //the number of frames the electrons have to resolve
int tiling_end = 2700; //give up on tiling after this frame

class Electronsphere{
  Electron[] electrons = new Electron[tnelectrons];
  Face[] faces = new Face[10000];
  int nelectrons = 0;
  int nfaces = 0;
  Electronsphere(){
    for(int i=0;i<tnelectrons;i++){
      electrons[nelectrons] = new Electron(random(-radius,radius),random(-radius,radius),random(-radius,radius));
      nelectrons++;
    }
  }
  void generate_face(){
    if(nfaces>=tnfaces){ return; }
    Electron[] ns = {electrons[floor(random(nelectrons-.0001))],electrons[floor(random(nelectrons-.0001))],electrons[floor(random(nelectrons-.0001))]};
    Electron[] ons = new Electron[3];
    int tries = 0;
    while(tries<10){
      tries++;
      arraycopy(ns,ons);
      Face f = new Face(ns);
      Point cp = f.get_circumcenter_point();
      float cm = mag(cp.x,cp.y,cp.z);
      if(cm<distance_tolerance){ return; }
      cp.x *= radius/cm; cp.y *= radius/cm; cp.z *= radius/cm;
      ns = get_closest_electrons(cp.x,cp.y,cp.z,3);
      if((ons[0]==ns[0]||ons[0]==ns[1]||ons[0]==ns[2])&&(ons[1]==ns[0]||ons[1]==ns[1]||ons[1]==ns[2])&&(ons[2]==ns[0]||ons[2]==ns[1]||ons[2]==ns[2])){
        f = new Face(ns);
        if(!f.is_acute()){ return; }
        for(int i=0;i<nfaces;i++){
          if((faces[i].p1==ns[0]||faces[i].p1==ns[1]||faces[i].p1==ns[2])&&(faces[i].p2==ns[0]||faces[i].p2==ns[1]||faces[i].p2==ns[2])&&(faces[i].p3==ns[0]||faces[i].p3==ns[1]||faces[i].p3==ns[2])){ return; }
        }
        f.orient(zero_point);
        faces[nfaces] = f;
        nfaces++;
        return;
      }
    }
  }
  Electron[] get_closest_electrons(float $x,float $y,float $z,int $n){
    Electron[] temp = new Electron[nelectrons];
    float[] ds = new float[nelectrons];
    //get distances to each Electron
    for(int i=0;i<nelectrons;i++){
      ds[i] = dist(electrons[i].x,electrons[i].y,electrons[i].z,$x,$y,$z);
      temp[i] = electrons[i];
    }
    //bubble sort according to distance
    for(int i=0;i<nelectrons-1;i++){
      for(int j=0;j<nelectrons-1-i;j++){
        if(ds[j]>ds[j+1]){
          Electron temp1 = temp[j]; temp[j] = temp[j+1]; temp[j+1] = temp1;
          float ds1 = ds[j]; ds[j] = ds[j+1]; ds[j+1] = ds1;
        }
      }
    }
    //chop it to match number desired
    temp = (Electron[]) subset(temp,0,$n);
    return temp;
  }
  void rotate(float $rx,float $ry){
    float cosrx = cos($rx); float sinrx = sin($rx);
    float cosry = cos($ry); float sinry = sin($ry);
    for(int i=0;i<nelectrons;i++){
      electrons[i].rotate(cosrx,sinrx,"x",0,0,0);
      electrons[i].rotate(cosry,sinry,"y",0,0,0);
    }
  }
  void step(){
    if(frame>tiling_start&&frame<tiling_end){
      generate_face();
    }    
  }
  void render(){
    if(frame<tiling_start){
      for(int i=0;i<nelectrons-1;i++){
        electrons[i].repel((Electron[]) subset(electrons,i+1,nelectrons-i-1));
      }
    }
    for(int i=0;i<nelectrons;i++){
      electrons[i].step();
      electrons[i].render();
    }
    for(int i=0;i<nfaces;i++){
      faces[i].render(true);
    }
  }
}
